//foodclerk.cc

#include "foodclerk.h"
#include "cineplex.h"

FoodClerk::FoodClerk(char* name, Line* line)
{
	myName = name;
	msgList = new MsgList();
	myThread = new Thread(myName);
	msgLock = new Lock(myName);
	sleepCon = new Condition(myName);
	myLine=line;
	status="Idle";
	moneyCollected = new float;
	*moneyCollected=0.0f;
	POPCORN_PRICE =5.00f;
	SODA_PRICE =4.00f;
	popcornRequested=0;
	sodaRequested=0;
	myLine->myClerk =(void *) this;
	myLine->myType=2;
	onBreak=false;
}

FoodClerk::~FoodClerk()
{

}

void
FoodClerk::Start()
{
	printf("Starting FoodClerk %s...\n", myName);
	if(getCineplex()->runSchedulers) Scheduler();
}

void
FoodClerk::SendMessage(char* name, int argc, ...)
{
	//printf("Acquiring msgLock in %s...\n",myName);
	msgLock->Acquire();
	//printf("Sending message in %s...\n",myName);
	
	
	IterList* args = new IterList();
	va_list vali;
	va_start(vali, argc);
	
	for ( int i = 0; i < argc; i++ )        
    {
        args->Append(va_arg ( vali, void* )); 
    }
	va_end(vali);
	
	msgList->Append(name, args);
	sleepCon->Signal(msgLock);
	
	msgLock->Release();
}

void
FoodClerk::SendMessage(char* name, IterList* args)
{
	msgLock->Acquire();
	
	msgList->Append(name, args);
	sleepCon->Signal(msgLock);
	
	msgLock->Release();
}

void
FoodClerk::Scheduler()
{
	bool runScheduler = getCineplex()->runSchedulers;
	do
	{
		//printf("%s hit scheduler\n",myName);
		msgLock->Acquire();
		IterList* msg;
		if(status=="TalkingToManager")
		{
			//Since the message had no arguments, I don't need to store it, just see if it is returned
			if(msgList->Find("Okay"))
			{
				ContinueOn();
				msgLock->Release();
				continue;
			}
			
			else if(msgList->Find("GoOnBreak"))
			{
				GoOnBreak();
				msgLock->Release();
				continue;
			}
			else if(msgList->Find("ComeOffBreak"))
			{
				ComeOffBreak();
				msgLock->Release();
				continue;
			}
		}
		else if(status=="WaitingForCust")
		{
			//I need to use the arguments, so I store the iterList
			msg=msgList->Find("WantPopcorn");
			if(msg)
			{
				AcceptPopcornRequest(msg->Get(0),msg->GetInt(1));
				msgLock->Release();
				continue;
			}
		}
		else if(status=="WaitingForSoda")
		{
			msg=msgList->Find("WantSoda");
			if(msg)
			{
				AcceptSodaRequest(msg->Get(0),msg->GetInt(1));
				msgLock->Release();
				continue;
			}
		}
		else if(status=="WaitingForPay")
		{
			msg=msgList->Find("HereIsFoodMoney");
			if(msg)
			{
				GiveFood(msg->Get(0),msg->GetFloat(1));
				msgLock->Release();
				continue;
			}
		}
		else if(status=="OnBreak")
		{
			msg=msgList->Find("WhatIsStatus");
			if(msg)
			{
				TellStatus(msg->Get(0));
				msgLock->Release();
				continue;
			}
			msg=msgList->Find("GiveMeMoney");
			if(msg)
			{
				GiveManagerMoney(msg->Get(0));
				msgLock->Release();
				continue;
			}
		}
		else if(status=="Idle")
		{
			msg=msgList->Find("WhatIsStatus");
			if(msg)
			{
				TellStatus(msg->Get(0));
				msgLock->Release();
				continue;
			}
			msg=msgList->Find("GiveMeMoney");
			if(msg)
			{
				GiveManagerMoney(msg->Get(0));
				msgLock->Release();
				continue;
			}
			else if(CallNextInLine())
			{
				msgLock->Release();
				continue;
			}
		}
		msgLock->Release();
		sleepCon->Wait(msgLock);
	} while(runScheduler);
}

//Gets the next customer in his concession line.  Returns true if there is one, false otherwise
bool
FoodClerk::CallNextInLine()
{
	getCineplex()->foodLinesLock->Acquire();
	Customer* cust=(Customer*)myLine->customers->Pop(0);
	getCineplex()->foodLinesLock->Release();
	if(cust)
	{
		printf("ConcessionClerk %s has a line length %i\n", myName, myLine->getSize());
		cust->SendMessage("ComeBuyFood",1,this);
		status="WaitingForCust";
		return true;
	}
	printf("ConcessionClerk %s has no one in line.  I am available for a customer\n", myName);
	return false;
}

//Accepts the amount of popcorn requested and asks if the customer wants soda
void
FoodClerk::AcceptPopcornRequest(void* custPtr, int amount)
{
	Customer* cust=(Customer*)custPtr;
	popcornRequested=amount;
	cust->SendMessage("AnythingElse",1,this);
	status="WaitingForSoda";	
}

//Accepts the amount of popcorn requested and asks for payment
void
FoodClerk::AcceptSodaRequest(void* custPtr, int amount)
{
	Customer* cust=(Customer*)custPtr;
	sodaRequested=amount;
	money=POPCORN_PRICE*popcornRequested+SODA_PRICE*amount;
	printf ("ConcessionClerk %s has an order for %i popcorn and %i soda. The cost is %.2f.\n",myName,popcornRequested,sodaRequested,money);
	cust->SendMessage("PayMeForFood",2,this,&money);
	status="WaitingForPay";	
}

//Gives the Customer the food and adds the money to his current take
void
FoodClerk::GiveFood(void* custPtr, float payment)
{
	printf ("ConcessionClerk %s has been paid for the order\n",myName);
	Customer* cust=(Customer*)custPtr;
	Popcorn* popcornPool = getCineplex()->popcornPool;
	Popcorn* popcorn = &(popcornPool[getCineplex()->popcornSold]);
	getCineplex()->popcornSold+=popcornRequested;
	
	Soda* sodaPool = getCineplex()->sodaPool;
	Soda* soda = &(sodaPool[getCineplex()->sodaSold]);
	getCineplex()->sodaSold+=sodaRequested;
		
	cust->SendMessage("HereIsYourFood",2,&popcorn,&soda);
	*moneyCollected+=payment;
	status="Idle";
}

void
FoodClerk::GiveManagerMoney(void* mngrPtr)
{
	Manager* manager=(Manager *)mngrPtr;
	manager->SendMessage("HereIsMoney",2,this,moneyCollected);
	moneyCollected = new float;
	*moneyCollected=0.0f;
	
}

//Tells the manager how many people are in his line.  If he is on break, he returns -1
void
FoodClerk::TellStatus(void* mngrPtr)
{
	Manager* manager=(Manager *)mngrPtr;
	int lineLength;
	if(status=="OnBreak")
	{
		lineLength=-1;
		onBreak=true;
	}
	else
	{
		lineLength=myLine->getSize();
		onBreak=false;
	}
	manager->SendMessage("NumInLine",2,this,&lineLength);
	status="TalkingToManager";
}

//The manager doesn't tell the clerk to change break status, so he returns to the previous state
void
FoodClerk::ContinueOn()
{
	
	if(onBreak)
	{
			status="OnBreak";
	}
	else
	{
		status="Idle";
	}
}

//Clerks goes on break
void
FoodClerk::GoOnBreak()
{
	
	printf("ConcessionClerk %s is going on break\n", myName);
	myLine->isOpen=0;
	myLine->Clear();
	status="OnBreak";
}

void
FoodClerk::ComeOffBreak()
{
	
	printf("ConcessionClerk %s is coming off break\n", myName);
	myLine->isOpen=1;
	status="Idle";
}
